//**************************************************
// Description: convert continuous 32-bit data to continuous 128-bit data 
//**************************************************

module tx_bus_convert #(
    parameter IN_MOD_WIDTH      = 4 ,
    parameter IN_DATA_WIDTH     = 128 ,
    parameter OUT_BYTE_PER_PHASE = 4 , // equal to "2^(OUT_MOD_WIDTH)"
    parameter OUT_MOD_WIDTH     = 2 ,
    parameter OUT_DATA_WIDTH    = 32
        ) (
    input                           clk     ,
    input                           rst_n   ,
    input   [9:0]                   ram_2p_cfg_register,
    // pre-module interface
	output							tx_bus_convert_rdy ,
	input  [IN_DATA_WIDTH-1:0]     	tx_bus_convert_data ,
    input  [IN_MOD_WIDTH-1:0]      	tx_bus_convert_mod ,
    input                          	tx_bus_convert_sav ,
    input                          	tx_bus_convert_val ,
    input                          	tx_bus_convert_sop ,
    input                          	tx_bus_convert_eop ,
	
  
    // post-module interface
	input                            mac_tx_rdy ,
    output  [OUT_DATA_WIDTH-1:0]     mac_tx_data ,
    output  [OUT_MOD_WIDTH-1:0]      mac_tx_mod ,
    output                           mac_tx_sav ,  //mac no need
    output                           mac_tx_val ,
    output                           mac_tx_sop ,
    output                           mac_tx_eop 
  
) ;
//**************************************************
// FIFO input 
//**************************************************

    //----------------------------------------------
    // gen frame_input_data_tmp(135-bit = sop + val + eop + mod + data)
    //----------------------------------------------   
	reg [1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:0] frame_input_data_tmp ;
	reg [1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:0] fifo_write_data ;
	reg fifo_write_en ;
	
	always @(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			frame_input_data_tmp <= {(1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH){1'b0}};
		end
		else begin 
			frame_input_data_tmp <= {tx_bus_convert_sop,tx_bus_convert_val,tx_bus_convert_eop,tx_bus_convert_mod,tx_bus_convert_data};
		end
	end
		
    //----------------------------------------------
    // signal declare
    //----------------------------------------------
 //   reg [255:0] fifo_depth_used_counter = 0 ;   //the depth of fifo has used
    //----------------------------------------------
    // FSM declare
    //----------------------------------------------
	reg [1:0] frame_in_state_c;
	reg [1:0] frame_in_state_n;
	localparam IN_IDLE        = 2'd0;
	localparam IN_WAIT_DATA   = 2'd1;
	localparam IN_DATA        = 2'd2;
	localparam IN_DATA_END    = 2'd3;
    
    // input FSM (1.1)
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) 
            frame_in_state_c <= IN_IDLE ;
        else
            frame_in_state_c <= frame_in_state_n ;
    end  
   
    // input FSM (1.2)
    always@(*) begin
        case(frame_in_state_c)
            IN_IDLE: 
				if(tx_bus_convert_sav )
					frame_in_state_n = IN_WAIT_DATA ;
				else 
					frame_in_state_n = IN_IDLE ;
           
            IN_WAIT_DATA: begin
				if(tx_bus_convert_sop && tx_bus_convert_val) begin
					frame_in_state_n = IN_DATA ;
				end
				else begin 
					frame_in_state_n = IN_WAIT_DATA ;
				end              
            end
            IN_DATA: begin
				if(tx_bus_convert_eop ) begin
					frame_in_state_n = IN_DATA_END ;
				end
				else begin
					frame_in_state_n = IN_DATA ;
				end 
          end
            IN_DATA_END: begin
				frame_in_state_n = IN_IDLE ;      
            end

            default: begin
				frame_in_state_n = IN_IDLE ;          
            end
        endcase
    end

    // input FSM (1.3)
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			fifo_write_data <= {(1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH){1'b0}} ;
			fifo_write_en   <= 1'b0 ;			
		end
		else begin
			case (frame_in_state_c)
				IN_IDLE,
				IN_WAIT_DATA: begin
					fifo_write_data <= {(1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH){1'b0}} ;
					fifo_write_en   <= 1'b0 ;	
				end
				IN_DATA,
				IN_DATA_END: begin
					fifo_write_data <= frame_input_data_tmp ;
					fifo_write_en   <= 1'b1 ;
		//			fifo_depth_used_counter <= fifo_depth_used_counter +1 ;
				end
				
				default:begin
					fifo_write_data <= {(1+1+IN_MOD_WIDTH+IN_DATA_WIDTH){1'b0}} ;
					fifo_write_en   <= 1'b0 ;
				end
			endcase
		end
	end
	
	// output assign
	assign tx_bus_convert_rdy = ( ((frame_in_state_c==IN_IDLE) || (frame_in_state_c==IN_WAIT_DATA)) ) ? 1'b1 : 1'b0 ;
  




//**************************************************
// FIFO output
//**************************************************
    //----------------------------------------------
    // signal declare
    //----------------------------------------------
    reg fifo_read_en ;
	reg fifo_read_en_delay ;
	wire [1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:0]fifo_read_data ;
	reg [1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:0]frame_output_data_tmp ;
	
	// FIFO empty signal
	wire fifo_empty ;
  
    //----------------------------------------------
    // FIFO is read every four clock cycles
    //----------------------------------------------
	reg [1:0] clk_counter ;
		
	//the cycle of clk_counter is 4
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) 
			clk_counter <= 2'b00 ;
		else if(clk_counter <= 2'b10)
			clk_counter <= clk_counter + 1 ;	
		else 
			clk_counter <= 2'b00 ;
	end
		
	//every four clock, if FIFO have data, rise fifo_read_en
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) 
			fifo_read_en <= 1'b0 ;
		else if(clk_counter == 2'b11) begin
			if(( fifo_empty != 1'b1  ) && (mac_tx_rdy == 1)&&(frame_output_data_tmp[1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b0))begin
				fifo_read_en <= 1'b1 ;	
			end
			else 
				fifo_read_en <= 1'b0 ;
		end 	
		else 
			fifo_read_en <= 1'b0 ;
		
	end
	
	//fifo_read_en_delay:the signal of fifo_read_data transfer to frame_output_data_tmp
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n)
			fifo_read_en_delay <= 1'b0 ;
		else
			fifo_read_en_delay <= fifo_read_en ;
	end
	
	
	
	//transfer data from fifo to frame_output_data_tmp,and make fifo_depth_used_counter -1
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) 
			frame_output_data_tmp <= 0 ;
		else if(clk_counter == 2'b01) begin  
			if((fifo_read_en_delay ) ) begin
			
				frame_output_data_tmp <= fifo_read_data ;
	//			if(fifo_write_en)  //read while write 
	//				fifo_depth_used_counter <= fifo_depth_used_counter ;
	//			else 
	//				fifo_depth_used_counter <= fifo_depth_used_counter -1 ;
			end
			else 
				frame_output_data_tmp <= 0 	;		
		end		
		else 
			frame_output_data_tmp <= frame_output_data_tmp ;
			
	end
  
    //----------------------------------------------
    // FSM declare
    //----------------------------------------------
	

    //----------------------------------------------
    // read enable
    //----------------------------------------------
    // output frame count declare
  
  
    // output FSM declare
 


//**************************************************
// FIFO instance
//**************************************************
`ifdef FPGA_MODE
   tx_bus_convert_fifo_256d_135w inst_tx_bus_convert_fifo_256d_135w(
        .clk            (clk),                  // input wire clk
        .rst           	(!rst_n),               // input wire rst

        .din            (fifo_write_data),      // input wire [134 : 0] din
        .wr_en          (fifo_write_en),        // input wire wr_en
        .rd_en          (fifo_read_en),         // input wire rd_en
        .dout           (fifo_read_data),       // output wire [134 : 0] dout
        .full           (),                     // output wire full
        .empty          (fifo_empty)                      // output wire empty
    ) ;
`else
   fifo_256d_135w_wrapper inst_tx_bus_convert_fifo_256d_135w(
        .clk            (clk),                  // input wire clk
        .rst      		(!rst_n),               // input wire rst
        .ram_2p_cfg_register (ram_2p_cfg_register),

        .din            (fifo_write_data),      // input wire [134 : 0] din
        .wr_en          (fifo_write_en),        // input wire wr_en
        .rd_en          (fifo_read_en),         // input wire rd_en
        .dout           (fifo_read_data),       // output wire [134 : 0] dout
        .full           (),                     // output wire full
        .empty          (fifo_empty)                      // output wire empty
    ) ;
`endif

//**************************************************
// FIFO output FSM
//**************************************************
    //----------------------------------------------
    // signal declare (above)
    //----------------------------------------------
	reg [OUT_DATA_WIDTH-1:0] frame_output_data ;
	reg [OUT_MOD_WIDTH-1:0] frame_output_mod ;
	reg frame_output_sav ;
	reg frame_output_val ;
	reg frame_output_sop ;
	reg frame_output_eop ;
    //----------------------------------------------
    // FSM declare (above)
    //----------------------------------------------
    reg [2:0] frame_out_state_c ;
    reg [2:0] frame_out_state_n ;
    localparam OUT_IDLE           = 3'd0 ;
    localparam OUT_REC_STAGE1     = 3'd1 ; // has sent 1st stage
    localparam OUT_REC_STAGE2     = 3'd2 ; // has sent 2st stage
    localparam OUT_REC_STAGE3     = 3'd3 ; // has sent 3nd stage
    localparam OUT_REC_STAGE4     = 3'd4 ; // has sent 4th stage
    localparam OUT_REC_END        = 3'd5 ;

    // output FSM (2.1)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            frame_out_state_c <= OUT_IDLE ;
        end
        else begin
            frame_out_state_c <= frame_out_state_n ;
        end
	end
   
    // output FSM (2.2)
       always@(*) begin                       
        case(frame_out_state_c)
            OUT_IDLE: begin
				if(frame_output_data_tmp[1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b1) //sop == 1
					frame_out_state_n = OUT_REC_STAGE1 ;
				else 
					frame_out_state_n = OUT_IDLE ;           
            end

            OUT_REC_STAGE1: begin
			
                frame_out_state_n = OUT_REC_STAGE2 ; 
            end

            OUT_REC_STAGE2: begin
            	
                frame_out_state_n = OUT_REC_STAGE3 ; 
            end

            OUT_REC_STAGE3: begin
				if(frame_output_data_tmp[1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b1) begin //val ==1
					if(frame_output_data_tmp[1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b1)   //eop == 1
						frame_out_state_n = OUT_REC_END ;
					else
						frame_out_state_n = OUT_REC_STAGE4 ;
				end
				else 
                frame_out_state_n = OUT_REC_STAGE3 ; 
            end

            OUT_REC_STAGE4: begin
				
                frame_out_state_n = OUT_REC_STAGE1 ; 
            end

            OUT_REC_END: begin
                frame_out_state_n = OUT_IDLE ;
            end

            default:
                frame_out_state_n = OUT_IDLE ;
        endcase
    end
    // output FSM (2.3)
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			frame_output_data <= 0 ;
			frame_output_mod  <= 0 ;			
			
			frame_output_sop  <= 0 ;
			frame_output_eop  <= 0 ;
		end
		else begin
			case(frame_out_state_c)
			OUT_IDLE: begin
				frame_output_data <= {frame_output_data_tmp[4*OUT_DATA_WIDTH-1:3*OUT_DATA_WIDTH]} ;
				
				frame_output_sop  <= frame_output_data_tmp[1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1];   //sop only equal 1 in first
					
				
			
		
				if(frame_output_data_tmp[4*OUT_DATA_WIDTH+3:4*OUT_DATA_WIDTH+2] == 2'b00)  begin  //mod =0000\0001\0010\0011
					frame_output_mod <= frame_output_data_tmp[4*OUT_DATA_WIDTH+1:4*OUT_DATA_WIDTH] ;
				end
				else 
					frame_output_mod <= 0 ;	
					
				if (frame_output_data_tmp[1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b1) begin//eop==1		
					case(frame_output_data_tmp[IN_MOD_WIDTH+4*OUT_DATA_WIDTH-1:4*OUT_DATA_WIDTH]) //mod = 0001\0010\0011\0100
						4'b0001,
						4'b0010,
						4'b0011,
						4'b0100:begin
							frame_output_eop <= 1 ;
						end
						default:
							frame_output_eop <= 0 ;
					endcase	
				end					
            end

            OUT_REC_STAGE1: begin
				frame_output_data <= {frame_output_data_tmp[3*OUT_DATA_WIDTH-1:2*OUT_DATA_WIDTH]} ;
				
				frame_output_sop  <= 0 ;
				
				if(frame_output_data_tmp[4*OUT_DATA_WIDTH+3:4*OUT_DATA_WIDTH+2] == 2'b01)  begin  //mod =0100\0101\0110\0111
					frame_output_mod <= frame_output_data_tmp[4*OUT_DATA_WIDTH+1:4*OUT_DATA_WIDTH] ;
				end
				else 
					frame_output_mod <= 0 ;		
					
				if (frame_output_data_tmp[1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b1) begin//eop==1		
					case(frame_output_data_tmp[IN_MOD_WIDTH+4*OUT_DATA_WIDTH-1:4*OUT_DATA_WIDTH]) //mod = 0101\0110\0111\1000
						4'b0101,
						4'b0110,
						4'b0111,
						4'b1000:begin
							frame_output_eop <= 1 ;
						end
						default:
							frame_output_eop <= 0 ;
					endcase	
				end					
           end

            OUT_REC_STAGE2: begin
            	frame_output_data <= {frame_output_data_tmp[2*OUT_DATA_WIDTH-1:OUT_DATA_WIDTH]} ;
				
				if(frame_output_data_tmp[4*OUT_DATA_WIDTH+3:4*OUT_DATA_WIDTH+2] == 2'b10)  begin  //mod =1000\1001\1010\1011
					frame_output_mod <= frame_output_data_tmp[4*OUT_DATA_WIDTH+1:4*OUT_DATA_WIDTH] ;
				end
				else 
					frame_output_mod <= 0 ;		   

				if (frame_output_data_tmp[1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b1) begin//eop==1		
					case(frame_output_data_tmp[IN_MOD_WIDTH+4*OUT_DATA_WIDTH-1:4*OUT_DATA_WIDTH]) //mod = 1001\1010\1011\1100
						4'b1001,
						4'b1010,
						4'b1011,
						4'b1100:begin
							frame_output_eop <= 1'b1 ;
						end
						default:
							frame_output_eop <= 1'b0 ;
					endcase	
				end					
            end

            OUT_REC_STAGE3: begin
				frame_output_data <= {frame_output_data_tmp[OUT_DATA_WIDTH-1:0]} ;
				
				if(frame_output_data_tmp[4*OUT_DATA_WIDTH+3:4*OUT_DATA_WIDTH+2] == 2'b11)  begin  //mod =1100\1101\1110\1111
					frame_output_mod <= frame_output_data_tmp[4*OUT_DATA_WIDTH+1:4*OUT_DATA_WIDTH] ;
				end
				else 
					frame_output_mod <= 0 ;		
					
				if (frame_output_data_tmp[1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b1) begin//eop==1		
					case(frame_output_data_tmp[IN_MOD_WIDTH+4*OUT_DATA_WIDTH-1:4*OUT_DATA_WIDTH]) //mod = 1101\1110\1111\0000
						4'b1101,
						4'b1110,
						4'b1111,
						4'b0000:begin
							frame_output_eop <= 1'b1 ;
						end
						default:
							frame_output_eop <= 1'b0 ;
					endcase	
				end						
            end

            OUT_REC_STAGE4: begin
				frame_output_data <= {frame_output_data_tmp[4*OUT_DATA_WIDTH-1:3*OUT_DATA_WIDTH]} ;
				
				if(frame_output_data_tmp[4*OUT_DATA_WIDTH+3:4*OUT_DATA_WIDTH+2] == 2'b00)  begin  //mod =0000\0001\0010\0011
					frame_output_mod <= frame_output_data_tmp[4*OUT_DATA_WIDTH+1:4*OUT_DATA_WIDTH] ;
				end
				else 
					frame_output_mod <= 0 ;	 

				if (frame_output_data_tmp[1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+IN_MOD_WIDTH+IN_DATA_WIDTH-1] == 1'b1) begin//eop==1		
					case(frame_output_data_tmp[IN_MOD_WIDTH+4*OUT_DATA_WIDTH-1:4*OUT_DATA_WIDTH]) //mod = 0001\0010\0011\0100
						4'b0001,
						4'b0010,
						4'b0011,
						4'b0100:begin
							frame_output_eop <= 1'b1 ;
						end
						default:
							frame_output_eop <= 1'b0 ;
					endcase	
				end		
            end
			
			default: begin //include OUT_REC_END
				frame_output_data <= 0 ;
				frame_output_mod  <= 0 ;
				frame_output_eop  <= 0 ;
			end
			endcase
				
			
		end
	end

 
   
	
    // output "sav" ,if fifo have data ,rise "sav" signal
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			frame_output_sav <= 1'b0 ;
		else if((!fifo_empty) || (frame_output_val))
			frame_output_sav <= 1'b1 ;
		else 
			frame_output_sav <= 1'b0 ;
	end
	
	// output "val" 
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			frame_output_val <= 1'b0 ;
		else if(frame_output_data_tmp[IN_MOD_WIDTH+4*OUT_DATA_WIDTH-1:4*OUT_DATA_WIDTH] != 4'b0000) begin   //mod!= 0 
			if (frame_output_eop )
				frame_output_val <= 1'b0 ;
			else 
				frame_output_val <= frame_output_val ;
		end
		else 
			frame_output_val <= frame_output_data_tmp[1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1];
			
			
	end
 
 
  

    // signal assign
	assign mac_tx_data = frame_output_data ;
    assign mac_tx_mod = frame_output_mod ;
    assign mac_tx_sav = frame_output_sav ;
    assign mac_tx_val = frame_output_val ;
    assign mac_tx_sop = frame_output_sop ;
    assign mac_tx_eop = frame_output_eop ;

 

endmodule
